package it.unipd.fbaron.mind_reader_squared.activities;

import it.unipd.fbaron.mind_reader_squared.R;
import it.unipd.fbaron.mind_reader_squared.activities.OptionsFragment.OnOptionsEventListener;
import it.unipd.fbaron.mind_reader_squared.activities.TabsFragment.OnTabEventListener;
import it.unipd.fbaron.mind_reader_squared.model.DataBaseHelper;
import it.unipd.fbaron.mind_reader_squared.model.SymbolDAO;
import it.unipd.fbaron.mind_reader_squared.model.SymbolLogic;

import java.util.ArrayList;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.FragmentTransaction;
import android.content.ContentUris;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;

public class ManageSymbolActivity extends BaseActivity implements
		OnOptionsEventListener, OnTabEventListener {

	// fragments of this activity
	private TabsFragment currTabsFragment;
	private OptionsFragment currOptionsFragment;
	private OptionsFragment nextOptionsFragment;

	// variables to control the state of changes (saved/unsaved changes)
	private boolean modified; // true->modified false->not modified
	private boolean changeTabLock;
	private int oldTabIndex;
	private boolean parentModified = false;

	// code for the image piker intent
	final static int IMAGE_PIKER_INTENT = 1;

	// dialogs
	static final int DIALOG_DELETE_CONFIRM_ID = 1;
	static final int DIALOG_INCORRECT_DATA_ID = 2;
	static final int DIALOG_DISCARD_CHANGES_ID = 3;
	static final int DIALOG_INCORRECT_PARENT_ID = 4;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.manage_symbol_layout);

		// initialize tab fragment object, this is the only fragment declared in
		// the layout.
		currTabsFragment = (TabsFragment) getFragmentManager()
				.findFragmentById(R.id.symbol_list);
		changeTabLock = false;

		// add summary fragment
		FragmentTransaction ft = getFragmentManager().beginTransaction();
		ft.replace(R.id.symbol_options,
				SummaryFragment.newInstance(profileId, DataBaseHelper.M1));
		ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
		ft.commit();
	}

	/*
	 * (TabsFragment event callback) Manage all fragment transactions to do when
	 * current tab is changed, also control if there are unsaved changes.
	 */
	@Override
	public void onChangeTab(int newTabIndex, int oldTabIndex) {
		this.oldTabIndex = oldTabIndex;
		if (currOptionsFragment != null) {
			if (!modified && !changeTabLock) {
				FragmentTransaction ft = getFragmentManager()
						.beginTransaction();
				if (newTabIndex == 0)
					ft.replace(R.id.symbol_options, SummaryFragment
							.newInstance(profileId, DataBaseHelper.M1));
				else
					ft.replace(R.id.symbol_options, SummaryFragment
							.newInstance(profileId, DataBaseHelper.M2));
				ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
				ft.commit();
				currOptionsFragment = null;
			} else {
				if (!changeTabLock) {
					changeTabLock = true;

					currTabsFragment.changeTab(oldTabIndex);

					nextOptionsFragment = null;
					ManageSymbolActivity.this.runOnUiThread(new Runnable() {
						@Override
						public void run() {
							showDialog(DIALOG_DISCARD_CHANGES_ID);
						}
					});
				}
			}
		}
		if (newTabIndex == 2 && !changeTabLock) {
			SymbolDAO s = new SymbolDAO();
			s.setProfileId(profileId);
			currOptionsFragment = OptionsFragment.newInstance(s);
			FragmentTransaction ft = getFragmentManager().beginTransaction();
			ft.replace(R.id.symbol_options, currOptionsFragment);
			ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
			ft.commit();
			currTabsFragment.setSideText(getResources().getString(
					R.string.new_symbol_desc));
		}
		if ((newTabIndex == 0 || newTabIndex == 1)
				&& currOptionsFragment == null) {
			FragmentTransaction ft = getFragmentManager().beginTransaction();
			if (newTabIndex == 0)
				ft.replace(R.id.symbol_options, SummaryFragment.newInstance(
						profileId, DataBaseHelper.M1));
			else
				ft.replace(R.id.symbol_options, SummaryFragment.newInstance(
						profileId, DataBaseHelper.M2));
			ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
			ft.commit();
		}
	}

	/*
	 * (TabsFragment event callback) If the symbol selected is not the current
	 * selected one, add the new OptionsFragment associated with the new symbol.
	 */
	@Override
	public void onSymbolSelected(int index, int tabIndex) {
		ArrayList<SymbolDAO> list = currTabsFragment.getSymbolList();
		SymbolDAO symbol = list.get(index);
		nextOptionsFragment = OptionsFragment.newInstance(symbol);

		// Verify if the new symbol is not the same
		if (currOptionsFragment != null
				&& nextOptionsFragment.getArguments().getInt("id") == currOptionsFragment
						.getArguments().getInt("id"))
			return;

		if (!modified) {
			FragmentTransaction ft = getFragmentManager().beginTransaction();
			ft.replace(R.id.symbol_options, nextOptionsFragment);
			ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
			ft.commit();
			currOptionsFragment = nextOptionsFragment;

			modified = false;
		} else {
			ManageSymbolActivity.this.runOnUiThread(new Runnable() {
				@Override
				public void run() {
					showDialog(DIALOG_DISCARD_CHANGES_ID);
				}
			});
		}
	}
	
	/*
	 * (TabsFragment event callback) Update activity after back button is selected on TabsFragment.
	 */
	@Override
	public void onBackSelected(int parent_id) {
		if (parent_id > 0) {
			SymbolDAO symbol = SymbolLogic.getInstance(dbHelper).getSymbolById(parent_id);
			nextOptionsFragment = OptionsFragment.newInstance(symbol);
		}
		else
			nextOptionsFragment = null;

		if (!modified) {
			FragmentTransaction ft = getFragmentManager().beginTransaction();
			if (nextOptionsFragment != null)
				ft.replace(R.id.symbol_options, nextOptionsFragment);
			else 
				ft.replace(R.id.symbol_options, SummaryFragment.newInstance(profileId, DataBaseHelper.M2));
			ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
			ft.commit();
			currOptionsFragment = nextOptionsFragment;

			modified = false;
		} else {
			ManageSymbolActivity.this.runOnUiThread(new Runnable() {
				@Override
				public void run() {
					showDialog(DIALOG_DISCARD_CHANGES_ID);
				}
			});
		}
	}

	/*
	 * (OptionsFragment event callback) After simple controls save all changes
	 * to database starting a new async task.
	 */
	@Override
	public void onSave(SymbolDAO symbol) {
		boolean isNewSymbol = false;
		final ArrayList<SymbolDAO> list_to_update;
		
		if (symbol.getId() < 1) {
			isNewSymbol = true;
			list_to_update = null;

			// Integrity control
			if (!symbol.isM1() && !symbol.isM2()) {
				ManageSymbolActivity.this.runOnUiThread(new Runnable() {
					@Override
					public void run() {
						showDialog(DIALOG_INCORRECT_DATA_ID);
					}
				});
				return;
			}
		}
		else {
			isNewSymbol = false;
			
			//Get list that must be updated after save
			if (symbol.getId() != currTabsFragment.getListParent()) 
				list_to_update = currTabsFragment.getSymbolList();
			else {
				//Must update the list where were the symbol before parent update
				SymbolDAO old_symbol = SymbolLogic.getInstance(dbHelper).getSymbolById(symbol.getId()); 
				list_to_update = SymbolLogic.getInstance(dbHelper).getM2SymbolList(profileId, old_symbol.getParent());
			}
			
			
			//If parent changed 
			if (currTabsFragment.getTabIndex() != 0 && parentModified) {
				//Verify it is a valid parent (not recursive)
				if (SymbolLogic.getInstance(dbHelper).isSymbolDescendant(profileId, symbol.getId(), symbol.getParent())) {
					ManageSymbolActivity.this.runOnUiThread(new Runnable() {
						@Override
						public void run() {
							showDialog(DIALOG_INCORRECT_PARENT_ID);
						}
					});
					return;
				}
				
				//remove symbol from the list
				boolean found = false;
				for (int i=0; i<list_to_update.size() && !found; i++) {
					SymbolDAO s = list_to_update.get(i);
					if (s.getId() == symbol.getId()) {
						list_to_update.remove(i);
						found = true;
					}
				}
				parentModified = false;
			}
		}
		
		AsyncTask<SymbolDAO, Void, Void> t = null;
		if (!isNewSymbol) {
			t = new AsyncTask<SymbolDAO, Void, Void>() {

				@Override
				protected Void doInBackground(SymbolDAO... params) {
					// Save symbol changes
					SymbolLogic.getInstance(dbHelper).saveChanges(params[0]);

					// Save position changes
					boolean mode;
					if (currTabsFragment.getTabIndex() == 0)
						mode = true;
					else
						mode = false;
					SymbolLogic.getInstance(dbHelper).saveSymbolsPos(
							list_to_update, mode);

					return null;
				}
			};
		} else {
			t = new AsyncTask<SymbolDAO, Void, Void>() {

				@Override
				protected Void doInBackground(SymbolDAO... params) {
					// Save new symbol and set the new id
					params[0].setId(SymbolLogic.getInstance(dbHelper)
							.newSymbol(params[0]));

					return null;
				}
			};
		}

		t.execute(symbol);

		try {
			t.get();
			currOptionsFragment.setModified(false);
			modified = false;
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (!isNewSymbol) {
			// Save changes in tabView
			currTabsFragment.saveChanges(symbol);
			if (symbol.getId() != currTabsFragment.getListParent())
				currTabsFragment.setSymbolList(list_to_update);
			if (list_to_update.size() == 0) {
				currTabsFragment.changeTab(0);
				currTabsFragment.changeTab(1);
			}
		} else {
			// Clean the insert symbol form
			SymbolDAO s = new SymbolDAO();
			s.setProfileId(profileId);
			currOptionsFragment = OptionsFragment.newInstance(s);
			FragmentTransaction ft = getFragmentManager().beginTransaction();
			ft.replace(R.id.symbol_options, currOptionsFragment);
			ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
			ft.commit();
			currTabsFragment.setSideText(getResources().getString(
					R.string.new_symbol_completed)
					+ " (" + symbol.getText() + ")");
		}

	}

	
	@Override
	public void onChangeParent(int symbol_id, int parent_id) {
		
		if (symbol_id >= 1 && currTabsFragment.getTabIndex() == 1) {
			int list_parent = currTabsFragment.getListParent();
			if (parent_id != list_parent) {
				parentModified = true;
			}
		}
	}
	
	
	/*
	 * (OptionsFragment event callback) Move up (decrease position) in the
	 * symbols list the symbol with the given id.
	 */
	@Override
	public void onMoveUp(final int symbol_id) {
		// View Changes
		int newPos = currTabsFragment.moveUpInList(symbol_id);
		if (newPos >= 0) {
			currOptionsFragment
					.decreasePos(currTabsFragment.getTabIndex() == 0);
			int listSize = currTabsFragment.getSymbolList().size();
			if (newPos == listSize - 2) {
				currOptionsFragment.setDownLimit(false);
			}
			if (newPos == 0) {
				currOptionsFragment.setUpLimit(true);
			}
		}
	}

	/*
	 * (OptionsFragment event callback) Move down (augment position) in the
	 * symbols list the symbol with the given id.
	 */
	@Override
	public void onMoveDown(int symbol_id) {
		// View Changes
		int newPos = currTabsFragment.moveDownInList(symbol_id);
		if (newPos >= 0) {
			currOptionsFragment.augmentPos(currTabsFragment.getTabIndex() == 0);
			int listSize = currTabsFragment.getSymbolList().size();
			if (newPos == listSize - 1) {
				currOptionsFragment.setDownLimit(true);
			}
			if (newPos == 1) {
				currOptionsFragment.setUpLimit(false);
			}
		}
	}

	/*
	 * (OptionsFragment event callback) Start confirm dialog, see deleteSymbol()
	 * for delete logic.
	 */
	@Override
	public void onDelete(int symbol_id) {
		ManageSymbolActivity.this.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				showDialog(DIALOG_DELETE_CONFIRM_ID);
			}
		});
	}

	/*
	 * Delete the selected symbol from the list of the selected modality. It
	 * works on background thread.
	 */
	private void deleteSymbol() {
		int currTabIndex = currTabsFragment.getTabIndex();
		boolean delM1 = false, delM2 = false;
		if (currTabIndex == 0) {
			delM1 = true;
		}
		if (currTabIndex == 1) {
			delM2 = true;
		}
		final boolean delM1Final = delM1;
		final boolean delM2Final = delM2;
		final int symbolId = currOptionsFragment.getArguments().getInt("id");

		// Database changes
		AsyncTask<Integer, Void, Void> t = null;
		t = new AsyncTask<Integer, Void, Void>() {

			@Override
			protected Void doInBackground(Integer... params) {
				SymbolLogic.getInstance(dbHelper).deleteSymbol(profileId,params[0],
						delM1Final, delM2Final);
				return null;
			}

		}.execute(symbolId);

		// View Changes
		FragmentTransaction ft = getFragmentManager().beginTransaction();
		ft.remove(currOptionsFragment);
		ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
		ft.commit();

		try {
			t.get();
			if (symbolId != currTabsFragment.getListParent()) 
				currTabsFragment.removeFromList(symbolId,true);
			else {
				currTabsFragment.changeTab(0);
				currTabsFragment.changeTab(1);
			}
			currOptionsFragment = null;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * (OptionsFragment event callback) Set modified variable true.
	 */
	@Override
	public void onModify() {
		modified = true;
	}

	/*
	 * (OptionsFragment event callback) Start an intent with action and category
	 * to open a file explorer. If there are more than one application, it shows
	 * a list of possible file explorers.
	 */
	@Override
	public void onLoadImage() {
		Intent intent = new Intent();
		intent.addCategory(Intent.CATEGORY_OPENABLE);
		intent.setType("image/jpeg");
		intent.setAction(Intent.ACTION_GET_CONTENT);
		startActivityForResult(Intent.createChooser(intent, "select file"),
				IMAGE_PIKER_INTENT);
	}

	/*
	 * Overridden to catch the result of file explorer intent.
	 */
	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == IMAGE_PIKER_INTENT && data != null) {
			String image_path = data.getData().getPath();

			if (image_path.contains(".")
					&& (image_path.endsWith(".jpg")
							|| image_path.endsWith(".png")
							|| image_path.endsWith(".jpeg") || image_path
							.endsWith(".JPG"))) {
				currOptionsFragment.setNewImage(image_path);
			} else if (!image_path.contains(".")) {
				Uri pictureUri = ContentUris.withAppendedId(
						MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
						Long.parseLong(data.getData().getLastPathSegment()));
				currOptionsFragment.setNewImage(pictureUri.toString());
			}
		}
	}

	public int getProfileId() {
		return profileId;
	}
	
	public int getCurrTabIndex() {
		return currTabsFragment.getTabIndex();
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		Dialog dialog;
		AlertDialog.Builder builder;
		switch (id) {
		case DIALOG_DELETE_CONFIRM_ID:
			String message = "";
			if (currTabsFragment.getTabIndex() == 0)
				message = getResources().getString(R.string.delete_m1_symbol_confirm);
			else
				message = getResources().getString(R.string.delete_m2_symbol_confirm);
			
			builder = new AlertDialog.Builder(this);
			builder.setMessage(
					message)
					.setCancelable(false)
					.setPositiveButton(getResources().getString(R.string.yes),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int id) {
									dialog.cancel();
									deleteSymbol();
								}
							})
					.setNegativeButton(getResources().getString(R.string.no),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int id) {
									dialog.cancel();
								}
							});
			dialog = builder.create();
			break;
		case DIALOG_DISCARD_CHANGES_ID:
			builder = new AlertDialog.Builder(this);
			builder.setMessage(
					getResources().getString(R.string.discard_changes_confirm))
					.setCancelable(false)
					.setPositiveButton(getResources().getString(R.string.yes),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int id) {
									currTabsFragment.discardChanges();
									modified = false;

									FragmentTransaction ft = getFragmentManager()
											.beginTransaction();
									if (nextOptionsFragment != null) {
										ft.replace(R.id.symbol_options,
												nextOptionsFragment);
									} else {
										if (currTabsFragment.getTabIndex() == 0)
											ft.replace(
													R.id.symbol_options,
													SummaryFragment
															.newInstance(
																	profileId,
																	DataBaseHelper.M1));
										else if (currTabsFragment.getTabIndex() == 1)
											ft.replace(
													R.id.symbol_options,
													SummaryFragment
															.newInstance(
																	profileId,
																	DataBaseHelper.M2));
										currTabsFragment.changeTab(oldTabIndex);
										changeTabLock = false;
									}
									ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
									ft.commit();

									currOptionsFragment = nextOptionsFragment;
									if (currOptionsFragment != null) {
										modified = false;
									}

									dialog.cancel();
								}
							})
					.setNegativeButton(getResources().getString(R.string.no),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int id) {
									if (nextOptionsFragment == null) {
										changeTabLock = false;
									}
									dialog.cancel();
								}
							});
			dialog = builder.create();
			break;
		case DIALOG_INCORRECT_DATA_ID:
			builder = new AlertDialog.Builder(this);
			builder.setMessage(
					getResources().getString(R.string.no_mode_selected))
					.setCancelable(false)
					.setPositiveButton(getResources().getString(R.string.ok),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int id) {
									dialog.cancel();
								}
							});
			dialog = builder.create();
			break;
		case DIALOG_INCORRECT_PARENT_ID:
			builder = new AlertDialog.Builder(this);
			builder.setMessage(
					getResources().getString(R.string.invalid_parent_selected))
					.setCancelable(false)
					.setPositiveButton(getResources().getString(R.string.ok),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int id) {
									dialog.cancel();
								}
							});
			dialog = builder.create();
			break;
		default:
			dialog = null;
		}

		if (dialog == null) {
			return super.onCreateDialog(id);
		} else
			return dialog;
	}

}
